#pragma once
#include <vector>
#include <pthread.h>
#include <semaphore.h>

const size_t N = 5;
template <typename T>
class RingQueue
{
private:
    size_t _cap;              // 容量上限
    sem_t _data_sem;          // 消费者关心的信号量
    sem_t _room_sem;          // 生产者关心的信号量
    pthread_mutex_t _con_mtx; // 消费者的锁
    pthread_mutex_t _pro_mtx; // 生产者的锁
    size_t _coni;             // 生产者当前下标
    size_t _proi;             // 消费者当前下标
    std::vector<T> _ring;     // 环形队列底层结构
public:
    RingQueue(size_t cap = N)
        : _cap(cap), _coni(0), _proi(0), _ring(cap)
    {
        sem_init(&_data_sem, 0, 0);    // 一开始数据是空的
        sem_init(&_room_sem, 0, _cap); // 一开始空间是满的
        pthread_mutex_init(&_con_mtx, nullptr);
        pthread_mutex_init(&_pro_mtx, nullptr);
    }

    ~RingQueue()
    {
        sem_destroy(&_data_sem);
        sem_destroy(&_room_sem);
        pthread_mutex_destroy(&_con_mtx);
        pthread_mutex_destroy(&_pro_mtx);
    }
    RingQueue(const RingQueue<T> &r) = delete;
    RingQueue<T> &operator=(const RingQueue<T> &r) = delete;
    void push(const T &in) // 生产者
    {
        P(_room_sem);   // 申请空间信号量
        Lock(_pro_mtx); // 维护生产者-生产者之间的互斥
        _ring[_proi++] = in;
        _proi %= _cap;
        Unlock(_pro_mtx); // 维护生产者-生产者之间的互斥
        V(_data_sem);     // 增加数据信号量
    }
    void pop(T &out) // 消费者
    {
        P(_data_sem);   // 申请数据信号量
        Lock(_con_mtx); // 维护消费者-消费者之间的互斥
        out = _ring[_coni++];
        _coni %= _cap;
        Unlock(_con_mtx); // 维护消费者-消费者之间的互斥
        V(_room_sem);     // 增加空间信号量
    }

private:
    // 申请信号量，若信号量s > 0，则申请成功并s--，若信号量s == 0，则申请失败并阻塞
    void P(sem_t &s) { sem_wait(&s); }
    // 信号量s++
    void V(sem_t &s) { sem_post(&s); }
    void Lock(pthread_mutex_t &mtx) { pthread_mutex_lock(&mtx); }
    void Unlock(pthread_mutex_t &mtx) { pthread_mutex_unlock(&mtx); }
};